package com.demo.file.service.impl;

import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.demo.file.entity.SegmentFile;
import com.demo.file.mapper.SegmentFileMapper;
import com.demo.file.service.ISegmentFileService;
import com.demo.file.util.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author alyy20
 * @since 2022-04-18
 */
@Slf4j
@Service
public class SegmentFileServiceImpl extends ServiceImpl<SegmentFileMapper, SegmentFile> implements ISegmentFileService {


    @Override
    public SegmentFile checkFile(String key) {
        LambdaQueryWrapper<SegmentFile> queryWrapper = new LambdaQueryWrapper<SegmentFile>();
        queryWrapper.eq(SegmentFile::getMd5Key, key);
        return this.getOne(queryWrapper);
    }

    @Override
    public boolean createFile(String originFileName, String savePath, int size, int segmentSize, String key) {
        String fileName = FileUtils.createUUIDFileName(originFileName);
        String saveFileName = FileUtils.createSaveFileName(key, originFileName);
        SegmentFile segmentFile = new SegmentFile();
        segmentFile.setFilePath(savePath + saveFileName);
        segmentFile.setFileName(fileName);
        segmentFile.setSize(size);
        segmentFile.setSegmentIndex(0);
        segmentFile.setSegmentSize(segmentSize);

        int total = size / segmentSize;
        if (size % segmentSize != 0) {
            total++;
        }
        segmentFile.setSegmentTotal(total);
        segmentFile.setMd5Key(key);

        return this.save(segmentFile);
    }

    @Override
    public boolean saveFile(MultipartFile file, String savePath, String key, String originFileName, int segmentIndex) {
        String saveFileName = FileUtils.createSaveFileName(key, originFileName);
        String segmentFileName = FileUtils.getSegmentName(saveFileName, segmentIndex);
        boolean saveSuccess = upload(file, savePath + segmentFileName);
        if (saveSuccess) {
            SegmentFile segmentFile = this.checkFile(key);
            segmentFile.setSegmentIndex(segmentFile.getSegmentIndex() + 1);
            return this.updateById(segmentFile);
        }
        return false;
    }

    @Override
    public boolean modify(SegmentFile segmentFile) {
        return this.updateById(segmentFile);
    }

    @Override
    public boolean mergeSegment(String key) {
        SegmentFile segmentFile = checkFile(key);
        int segmentCount = segmentFile.getSegmentTotal();
        FileInputStream fileInputStream = null;
        FileOutputStream outputStream = null;
        byte[] byt = new byte[10 * 1024 * 1024];
        try {
            //分片合并
            File newFile = new File(segmentFile.getFilePath());
            outputStream = new FileOutputStream(newFile, true);
            int len;
            for (int i = 0; i < segmentCount; i++) {
                String segmentFilePath = FileUtils.getSegmentName(segmentFile.getFilePath(), i + 1);
                fileInputStream = new FileInputStream(new File(segmentFilePath));
                while ((len = fileInputStream.read(byt)) != -1) {
                    outputStream.write(byt, 0, len);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return false;
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
                log.info("IO流关闭");
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        log.info("分片合并成功");
        return true;
    }

    @Override
    public void deleteSegments(String key) throws InterruptedException {
        // 为了保证不被占用，先回收数据流对象
        System.gc();
        Thread.sleep(1000);
        SegmentFile segmentFile = checkFile(key);
        int segmentCount = segmentFile.getSegmentTotal();
        ArrayList<String> remain = new ArrayList<>();
        int finished = 0;
        int[] visited = new int[segmentCount];
        for (int i = 0; i < segmentCount; i++) {
            String segmentFilePath = FileUtils.getSegmentName(segmentFile.getFilePath(), i + 1);
            remain.add(segmentFilePath);
            File file = new File(segmentFilePath);
            boolean result = file.delete();
            if (result) {
                finished++;
                visited[i] = 1;
            }
            log.info("分片文件{}删除{}", segmentFilePath, (result ? "成功" : "失败"));
        }
        while (finished < segmentCount) {
            System.gc();
            Thread.sleep(1000);
            for (int i = 0; i < segmentCount; i++) {
                if (visited[i] == 0) {
                    String segmentFilePath = FileUtils.getSegmentName(segmentFile.getFilePath(), i + 1);
                    remain.add(segmentFilePath);
                    File file = new File(segmentFilePath);
                    boolean result = file.delete();
                    if (result) {
                        visited[i] = 1;
                        finished++;
                    }
                    log.info("分片文件{}删除{}", segmentFilePath, (result ? "成功" : "失败"));
                }
            }
        }
    }

    /**
     * 文件存储
     *
     * @param file
     * @param path
     * @return
     */
    private boolean upload(MultipartFile file, String path) {
        try {
            File dest = FileUtil.touch(path);
            file.transferTo(dest);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
